package vis.items.ds.stack;

import java.awt.Color;

import processing.core.PApplet;
import vis.app.controller.CodeController;
import vis.app.utils.Utils;
import vis.items.visualItem.IVisualObject;
import vis.items.visualItem.VisualObject;

public class Stack extends VisualObject {
	public static int STACK_SIZE = 5;
	public static int STACK_WIDTH = StackElem.ELEM_SIZE;
	public static int STACK_HEIGHT = STACK_SIZE * StackElem.ELEM_SIZE;
	public static int BOTTOM_MARGIN = 50;
	public static int TOP_MARGIN = 100;

	private int top;
	private StackElem[] data;

	public Stack(PApplet root, IVisualObject parent) {
		super(root, parent);
		setInfo();
		initLogic();
	}

	private void setInfo() {
		int initX = (root.width - StackElem.ELEM_SIZE) / 2;
		int initY = root.height - STACK_HEIGHT - BOTTOM_MARGIN;
		this.at(initX, initY).size(STACK_WIDTH, STACK_HEIGHT).color(Color.white);
	}

	private void initLogic() {
		data = new StackElem[STACK_SIZE];
		top = 0;

		monitorStack();
	}

	@Override
	public void displayItself() {
		super.displayItself();
		_displaySeperateLine();
	}

	private void _displaySeperateLine() {
		root.stroke(255);
		{
			for (int i = 1; i < STACK_SIZE; ++i) {
				int fromX = this.x;
				int fromY = this.y + i * StackElem.ELEM_SIZE;
				int toX = this.x + StackElem.ELEM_SIZE;
				int toY = fromY;
				root.line(fromX, fromY, toX, toY);
			}
		}
		root.noStroke();
	}

	/*
	 * For Stack Logic
	 */

	private boolean isEmpty() {
		isEmpty_visual();
		return 0 == top;
	}

	private void isEmpty_visual() {
		String jobs[] = { "14", "15", "16" };
		CodeController.getInstance().addHighlightJob(jobs);
	}

	private boolean isFull() {
		isFull_visual();
		return top == STACK_SIZE;
	}

	private void isFull_visual() {
		String jobs[] = { "18", "19", "20" };
		CodeController.getInstance().addHighlightJob(jobs);
	}

	private void push(String value) {
		int currentIndex = top;
		StackElem e = new StackElem(root, this, value, currentIndex);
		data[top] = e;
		top++;

		push_visual(e);
	}

	private void push_visual(StackElem e) {
		addChild(e.at(0, -TOP_MARGIN));
		String jobs[] = { "28", "29" };
		CodeController.getInstance().addHighlightJob(jobs);
		monitorStack();
	}

	private String pop() {
		top--;
		StackElem e = data[top];
		String value = e.value;

		pop_visual(e);

		return value;
	}

	private void pop_visual(StackElem e) {
		// removeChild(e);
		e.visualPopElement();

		String jobs[] = { "38", "39", "40" };
		CodeController.getInstance().addHighlightJob(jobs);
		monitorStack();
	}

	/*
	 * For Stack Visual
	 */

	public void doPush() {
		CodeController.getInstance().startPushJobs();
		{
			CodeController.getInstance().addHighlightJob(new String[] { "22" });
			CodeController.getInstance().addHighlightJob(new String[] { "23" });
			if (isFull()) {
				System.err.println("STACK FULL");
				CodeController.getInstance().addHighlightJob(
						new String[] { "24", "25" });
				return;
			}

			String value = String.valueOf(Utils.randomInt(999));
			push(value);
		}
		CodeController.getInstance().endPushJobs();
	}

	public void doPop() {
		CodeController.getInstance().startPushJobs();
		{
			CodeController.getInstance().addHighlightJob(new String[] { "32" });
			CodeController.getInstance().addHighlightJob(new String[] { "33" });
			if (isEmpty()) {
				System.err.println("STACK EMPTY");
				CodeController.getInstance().addHighlightJob(
						new String[] { "34", "35" });
				return;
			}
		}
		CodeController.getInstance().endPushJobs();

		String value = pop();
		monitor("popValue", value);
	}

	public String toString() {
		String s = "{";
		for (int i = 0; i < top; ++i) {
			s += data[i].value + ",";
		}
		return s + "}";
	}

	private void monitor(String varName, String value) {
		CodeController.getInstance().addDebugVariable(varName, value);
	}

	private void monitorTop() {
		monitor("top", String.valueOf(top));
	}

	private void monitorData() {
		monitor("data", this.toString());
	}

	private void monitorStack() {
		monitorTop();
		monitorData();
		// monitor("isFull", String.valueOf(isFull()));
		// monitor("isEmpty", String.valueOf(isEmpty()));
		monitor("popValue", "0");
	}
	// private void monitorErrorStackFull() {
	// monitor("error", "STACK FULL");
	// }
	//
	// private void monitorErrorStackEmpty() {
	// monitor("error", "STACK EMPTY");
	// }
}
